﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using System.Web;

namespace Kojarskiy.Core
{
    public static class Extensions
    {
        public static string StripHtml(this string htmlString)
        {
            string pattern = @"<(.|\n)*?>";

            return Regex.Replace(htmlString, pattern, string.Empty)
                .Replace("&#39;", "'").Replace("&quot;", "\"").Replace("&nbsp;", " ")
                .Replace("&lsquo;", "'").Replace("&rsquo;","'")
                .Replace("&ndash;", " - ").Replace("&ldquo;", "\"").Replace("&rdquo;", "\"").Replace("&laquo;", "\"").Replace("&raquo;", "\"");
        }

        public static List<string> FetchImages(this string htmlString)
        {
            var links = new List<string>();
            string regexImgSrc = @"<img[^>]*?src\s*=\s*[""']?([^'"" >]+?)[ '""][^>]*?>";
            MatchCollection matchesImgSrc = Regex.Matches(htmlString, regexImgSrc, RegexOptions.IgnoreCase | RegexOptions.Singleline);
            foreach (Match m in matchesImgSrc)
            {
                string href = m.Groups[1].Value;
                
                links.Add(href);
            }
            return links;
        }

        public static string ToPriceString(this decimal value)
        {
            return string.Format("{0:0.00} грн", value);
        }
        public static string ToPriceString(this decimal? value)
        {
            return string.Format("{0:0.00} грн", value);
        }

        public static IQueryable<T> Search<T>(this IQueryable<T> source, string searchTerm, params Expression<Func<T, string>>[] stringProperties)
        {
            if (String.IsNullOrEmpty(searchTerm))
            {
                return source;
            }

            var searchTermExpression = Expression.Constant(searchTerm);

            //Variable to hold merged 'OR' expression  
            Expression orExpression = null;
            //Retrieve first parameter to use accross all expressions  
            var singleParameter = stringProperties[0].Parameters.Single();

            //Build a contains expression for each property  
            foreach (var stringProperty in stringProperties)
            {
                //Syncronise single parameter accross each property  
                var swappedParamExpression = SwapExpressionVisitor.Swap(stringProperty, stringProperty.Parameters.Single(), singleParameter);

                //Build expression to represent x.[propertyX].Contains(searchTerm)  
                var containsExpression = BuildContainsExpression(swappedParamExpression, searchTermExpression);

                orExpression = BuildOrExpression(orExpression, containsExpression);
            }

            var completeExpression = Expression.Lambda<Func<T, bool>>(orExpression, singleParameter);
            return source.Where(completeExpression);
        }

        private static Expression BuildOrExpression(Expression existingExpression, Expression expressionToAdd)
        {
            if (existingExpression == null)
            {
                return expressionToAdd;
            }

            //Build 'OR' expression for each property  
            return Expression.OrElse(existingExpression, expressionToAdd);
        }

        private static MethodCallExpression BuildContainsExpression<T>(Expression<Func<T, string>> stringProperty, ConstantExpression searchTermExpression)
        {
            return Expression.Call(stringProperty.Body, typeof(string).GetMethod("Contains"), searchTermExpression);
        }

        internal class SwapExpressionVisitor : ExpressionVisitor
        {
            private readonly Expression from, to;
            private SwapExpressionVisitor(Expression from, Expression to)
            {
                this.@from = @from;
                this.to = to;
            }

            public static Expression<T> Swap<T>(Expression<T> lambda, Expression from, Expression to)
            {
                return Expression.Lambda<T>(
                    Swap(lambda.Body, from, to), lambda.Parameters);
            }

            private static Expression Swap(Expression body, Expression from, Expression to)
            {
                return new SwapExpressionVisitor(from, to).Visit(body);
            }

            public override Expression Visit(Expression node)
            {
                return node == this.@from ? this.to : base.Visit(node);
            }
        }    
    }
}